package cn.lhl.core.demo.emoji.filter.cfg.emoji;

import com.vdurmont.emoji.EmojiParser;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;

/**
 * 处理提交数据中的表情符号 filter
 *
 * @author liuhongliang
 * @version 2021/9/1
 */
@Slf4j
public class EmojiFilter implements Filter {

  public EmojiFilter(EmojiProp emojiProp) {
    this.emojiProp = emojiProp;
  }

  private final EmojiProp emojiProp;

  @Override
  public void init(FilterConfig filterConfig) throws ServletException {
    log.info("EmojiFilter init emojiProp={}", emojiProp);
  }

  @Override
  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
      throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    String method = request.getMethod();
    String contentType = request.getContentType();
    String logPrefix = "EmojiFilter doFilter";
    boolean flag = HttpMethod.GET.name().equals(method.toUpperCase())
        || emojiProp.getFilteredMediaTypeList().contains(contentType == null ? null : contentType.toLowerCase());
    if (!flag) {
      // 有些请求无需处理 如 上传文件
      log.debug("{} 无需处理 method={} contentType={}", logPrefix, method, contentType);
      filterChain.doFilter(servletRequest, servletResponse);
      return;
    }
    String srvPath = request.getServletPath();
    List<String> excludedUriList = emojiProp.getExcludedUriList();
    if (CollectionUtils.isNotEmpty(excludedUriList)) {
      long count = excludedUriList.stream().filter(e -> srvPath.startsWith(e)).count();
      if (count > 0) {
        // 在白名单中 直接放行
        log.debug("{} 在白名单中 直接放行", logPrefix);
        filterChain.doFilter(servletRequest, servletResponse);
        return;
      }
    }
    filterChain.doFilter(new ReqWrapper(request, emojiProp.getCharset()), servletResponse);
  }

  @Override
  public void destroy() {

  }

  public static class ReqWrapper extends HttpServletRequestWrapper {

    public ReqWrapper(HttpServletRequest request, String charset) {
      super(request);
      this.charset = charset;
    }

    private final String charset;

    @Override
    public ServletInputStream getInputStream() throws IOException {
      ServletInputStream inputStream = super.getInputStream();
      try (BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, charset))) {
        String line = null;
        StringBuilder result = new StringBuilder();
        while ((line = br.readLine()) != null) {
          result.append(clean(line));
        }
        return new ServletInputStreamWrapper(new ByteArrayInputStream(result.toString()
            .getBytes(Charset.forName(charset))));
      }
    }

    @Override
    public String getParameter(String name) {
      return clean(super.getParameter(name));
    }

    @Override
    public String[] getParameterValues(String name) {
      return clean(super.getParameterValues(name));
    }

    @Override
    public Map<String, String[]> getParameterMap() {
      return clean(super.getParameterMap());
    }

    private Map<String, String[]> clean(Map<String, String[]> map) {
      if (map == null) {
        return map;
      }
      // req 中的map不可直接修改需要新创建一个map
      Map<String, String[]> result = new LinkedHashMap<>();
      for (Map.Entry<String, String[]> me : map.entrySet()) {
        result.put(me.getKey(), clean(me.getValue()));
      }
      return Collections.unmodifiableMap(result);
    }

    private String[] clean(String[] arr) {
      if (arr != null) {
        for (int i = 0; i < arr.length; i++) {
          arr[i] = clean(arr[i]);
        }
      }
      return arr;
    }

    private String clean(String val) {
      if (StringUtils.isEmpty(val)) {
        return val;
      }
      return EmojiParser.removeAllEmojis(val);
    }
  }

  public static class ServletInputStreamWrapper extends ServletInputStream {

    public ServletInputStreamWrapper(InputStream stream) {
      this.stream = stream;
    }

    private InputStream stream;

    @Override
    public boolean isFinished() {
      return true;
    }

    @Override
    public boolean isReady() {
      return true;
    }

    @Override
    public void setReadListener(ReadListener listener) {

    }

    @Override
    public int read() throws IOException {
      return stream.read();
    }
  }
}
